// PatchClusterDlg.cpp : implementation file
//

#include "stdafx.h"
#include "PatchCluster.h"
#include "PatchClusterDlg.h"
#include "FeatureExtract/HistFeature.h"
#include "FeatureExtract/MomentFeature.h"
#include "Data/CodeBookGenerator.h"
#include "Cluster/KmeansCluster.h"
#include "Xml/TextFile.h"
#include "atlimage.h"
#include "gdiplus.h"
#pragma comment(lib, "gdiplus.lib")

#ifdef _DEBUG
#define new DEBUG_NEW
#endif


// CAboutDlg dialog used for App About

class CAboutDlg : public CDialog
{
public:
	CAboutDlg();

// Dialog Data
	enum { IDD = IDD_ABOUTBOX };

	protected:
	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support

// Implementation
protected:
	DECLARE_MESSAGE_MAP()
};

CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
{
}

void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
}

BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
END_MESSAGE_MAP()

/*==================================================================*/
// CPatchClusterDlg dialog
/*==================================================================*/

void CPatchClusterDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	DDX_Control(pDX, IDC_CMBFEATURE, m_cmbFeature);
	DDX_Control(pDX, IDC_TXTFOLDER, m_txtImgFolder);
	DDX_Control(pDX, IDC_TXTCLUSTERCNT, m_txtClusterCount);
	DDX_Control(pDX, IDC_SPINCLUSTERCNT, m_spinClusterCount);
	DDX_Control(pDX, IDC_TXTITERCOUNT, m_txtIterationCount);
	DDX_Control(pDX, IDC_SPINITERCOUNT, m_spinIterationCount);
	DDX_Control(pDX, IDC_TXTEPSILON, m_txtEpsilon);
	DDX_Control(pDX, IDC_LSTREPRESANTATIVE, m_lstRepresentative);
	DDX_Control(pDX, IDC_LSTCODEBOOK, m_lstCodeBook);
	DDX_Control(pDX, IDC_TXTXMLFILE, m_txtXmlFile);
	DDX_Control(pDX, IDC_BTNBROWSEXMLFILE, m_btnXmlFile);
	DDX_Control(pDX, IDC_TXTOUPUTFOLDER, m_txtCodeBookPath);
	DDX_Control(pDX, IDC_SPINPATCHSIZE, m_spinPatchSize);
	DDX_Control(pDX, IDC_TXTPATCHSIZE, m_txtPatchSize);
	DDX_Control(pDX, IDC_PICEDITOR, m_picFrame);
	DDX_Control(pDX, IDC_BTNAPPROVE, m_btnStartFeature);
	DDX_Control(pDX, IDC_BTNCLEAR, m_btnClearHotRegion);
	DDX_Control(pDX, IDC_BTNLOADSAMPLES, m_btnLoadSamples);
	DDX_Control(pDX, IDC_BTNSAVESAMPLE, m_btnSaveSamples);
}

BEGIN_MESSAGE_MAP(CPatchClusterDlg, CDialog)
	ON_WM_SYSCOMMAND()
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	//}}AFX_MSG_MAP
	ON_BN_CLICKED(IDC_BTNBROWSEFOLDER, &CPatchClusterDlg::OnBnClickedBtnbrowsefolder)
	ON_BN_CLICKED(IDOK, &CPatchClusterDlg::OnBnClickedOk)
	ON_NOTIFY(LVN_ITEMACTIVATE, IDC_LSTREPRESANTATIVE, &CPatchClusterDlg::OnLvnItemActivateLstrepresantative)
	ON_BN_CLICKED(IDC_BTNBROWSEXMLFILE, &CPatchClusterDlg::OnBnClickedBtnbrowsexmlfile)
	ON_BN_CLICKED(IDC_BTNBROWSEOUTPUTFOLDER, &CPatchClusterDlg::OnBnClickedBtnbrowseoutputfolder)
	ON_BN_CLICKED(IDC_BTNAPPROVE, &CPatchClusterDlg::OnBnClickedBtnapprove)
	ON_BN_CLICKED(IDC_BTNCLEAR, &CPatchClusterDlg::OnBnClickedBtnclear)
	ON_BN_CLICKED(IDC_BTNSTARTCLUSTER, &CPatchClusterDlg::OnBnClickedBtnstartcluster)
	ON_BN_CLICKED(IDC_BTNLOADSAMPLES, &CPatchClusterDlg::OnBnClickedBtnloadsamples)
	ON_BN_CLICKED(IDC_BTNSAVESAMPLE, &CPatchClusterDlg::OnBnClickedBtnsavesample)
END_MESSAGE_MAP()

CPatchClusterDlg::CPatchClusterDlg(CWnd* pParent /*=NULL*/)
: CDialog(CPatchClusterDlg::IDD, pParent),
m_codeBook(NULL)
{
	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

CPatchClusterDlg::~CPatchClusterDlg()
{
	if(m_codeBook)
		delete m_codeBook;
}

/*==================================================================*/

/*==================================================================*/

BOOL CPatchClusterDlg::LoadImageToEditor(CString& path)
{
	Gdiplus::Bitmap bmp(path);
	if(bmp.GetLastStatus() != Gdiplus::Ok)
		return FALSE;

	HBITMAP hBmp;
	bmp.GetHBITMAP(Gdiplus::Color(0, 0, 0), &hBmp);

	if(!hBmp)
		return FALSE;

	HBITMAP hBmpOld = m_picFrame.SetBitmap(hBmp);
	if(hBmpOld)
		::DeleteObject(hBmpOld);
	m_picFrame.ClearHotRegion();

	return TRUE;
}

void CPatchClusterDlg::ProcessImageRegion()
{
	CRect rcHotRegion;
	m_picFrame.GetHotRegion(rcHotRegion);

	if(rcHotRegion.left >= rcHotRegion.right || rcHotRegion.top >= rcHotRegion.bottom)
		return;

	CFeatureExtract* extractor;
	if(m_cmbFeature.GetCurSel() == 0)
	{
		extractor = new CHistFeature();
		((CHistFeature*)extractor)->SetHistSize(32);
	}
	else
	{
		extractor = new CMomentFeature();
	}

	HDC hDCSrc, hDCRef, hDCDest;
	HBITMAP hBmpSrc, hBmpSrcOld, hBmpDest, hBmpDestOld;

	hBmpSrc = m_picFrame.GetBitmap();
	hDCRef = ::GetDC(0);
	hDCSrc = ::CreateCompatibleDC(hDCRef);
	hBmpSrcOld = (HBITMAP)::SelectObject(hDCSrc, hBmpSrc);

	int iPatchSize = m_spinPatchSize.GetPos32();
	hBmpDest = ::CreateCompatibleBitmap(hDCRef, iPatchSize, iPatchSize);
	hDCDest = ::CreateCompatibleDC(hDCRef);
	hBmpDestOld = (HBITMAP)::SelectObject(hDCDest, hBmpDest);

	CString sFileNamePrefix, sSrcFile;
	m_txtCodeBookPath.GetWindowText(sFileNamePrefix);
	sSrcFile = m_fileFinder.GetFilePath();
	sSrcFile = sSrcFile.Mid(1 + sSrcFile.ReverseFind(L'\\'));
	sSrcFile = sSrcFile.Mid(0, sSrcFile.Find(L'.'));

	sFileNamePrefix += (sFileNamePrefix[sFileNamePrefix.GetLength() - 1] == L'\\' ?
		L"" : L"\\");
	sFileNamePrefix += sSrcFile;

	for(int i = rcHotRegion.left; i < rcHotRegion.right + iPatchSize; i += iPatchSize)
	{
		for(int j = rcHotRegion.top; j < rcHotRegion.bottom + iPatchSize; j += iPatchSize)
		{
			// paint to the new bitmap
			::BitBlt(hDCDest, 0, 0, iPatchSize, iPatchSize,
				hDCSrc, i, j, SRCCOPY);

			CString sFileName;
			sFileName.Format(L"%s_%d_%d.png", sFileNamePrefix, i, j);
			// save it
			CImage img;
			img.Attach(hBmpDest);
			img.Save(sFileName);
			img.Detach();

			// calculate feature vector
			CArray<double> vecFeature;
			extractor->Calc(sFileName, vecFeature);
			m_arrFeatures.Add(new CPatchSample(sFileName, vecFeature));
		}
	}
	::SelectObject(hDCDest, hBmpDestOld);
	::DeleteDC(hDCDest);
	::DeleteObject(hBmpDest);

	::ReleaseDC(NULL, hDCRef);
	::SelectObject(hDCSrc, hBmpSrcOld);
	::DeleteDC(hDCSrc);

	delete extractor;
}

CString CPatchClusterDlg::BrowseFolder(CString strTitle)
{
	CString strDisplayName;
	CString strPath;

	BROWSEINFO bi;

	bi.hwndOwner = m_hWnd; // Handle of the owner window
	bi.pidlRoot = NULL; // Desktop folder is used
	bi.lpszTitle = strTitle.GetString(); // Title of the dialog box
	bi.pszDisplayName = strDisplayName.GetBuffer(MAX_PATH); // Buffer for selected folder name
	bi.ulFlags = BIF_RETURNONLYFSDIRS; // Only returns file system directories
	bi.lpfn = NULL;
	bi.lParam = 0;

	LPITEMIDLIST pItemIDList = SHBrowseForFolder(&bi);

	strDisplayName.ReleaseBuffer();

	if (pItemIDList)
	{
		if (SHGetPathFromIDList(pItemIDList, strPath.GetBuffer(MAX_PATH)))
		{
			//SetWindowText(strPath);
		}

		// Avoid memory leaks by deleting the PIDL using the shell's task allocator
		IMalloc* pMalloc = NULL;
		if (SHGetMalloc(&pMalloc) != NOERROR)
		{
			TRACE("Failed to get pointer to shells task allocator");
			return L"";
		}

		pMalloc->Free(pItemIDList);
		if (pMalloc)
			pMalloc->Release();
	}

	strPath.ReleaseBuffer();

	return strPath;
}

void CPatchClusterDlg::EmptyImgList(CImageList& imgList)
{
	for(int i = imgList.GetImageCount() - 1; i >= 0; --i)
	{
		imgList.Remove(0);
	}
}

void CPatchClusterDlg::RenderCodeBook()
{
	m_lstCodeBook.DeleteAllItems();
	m_lstRepresentative.DeleteAllItems();
	EmptyImgList(m_imlCodeBook);
	EmptyImgList(m_imlRepresentative);

	m_lstRepresentative.SetImageList(&m_imlRepresentative, LVSIL_NORMAL);
	m_lstRepresentative.SetImageList(&m_imlRepresentative, LVSIL_SMALL);

	for(int i = m_codeBook->Entries.GetCount() - 1; i >= 0; --i)
	{
		CCodeBookEntry* entry = m_codeBook->Entries[i];
		CPatchSample* representative = entry->Words[entry->Representative];
		
		HBITMAP hBmp;
		Gdiplus::Bitmap bmp(representative->SamplePath);
		bmp.GetHBITMAP(Gdiplus::Color(0, 0, 0), &hBmp);

		int iImgIdx = ImageList_Add(m_imlRepresentative.GetSafeHandle(), hBmp, NULL);
		
		CString sFileName, sCluster, sCount;
		sFileName = representative->SamplePath;
		sFileName = sFileName.Mid(1+sFileName.ReverseFind(L'\\'));
		sCluster.Format(L"%d", i);
		sCount.Format(L"%d", entry->Words.GetCount());

		m_lstRepresentative.InsertItem(0, sFileName, iImgIdx);
		m_lstRepresentative.SetItemText(0, 1, sCluster);
		m_lstRepresentative.SetItemText(0, 2, sCount);
		m_lstRepresentative.SetItemText(0, 3, representative->GetFeatureString());
	}
}

/*==================================================================*/
// CPatchClusterDlg message handlers
/*==================================================================*/

BOOL CPatchClusterDlg::OnInitDialog()
{
	CDialog::OnInitDialog();

	// Add "About..." menu item to system menu.

	// IDM_ABOUTBOX must be in the system command range.
	ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
	ASSERT(IDM_ABOUTBOX < 0xF000);

	CMenu* pSysMenu = GetSystemMenu(FALSE);
	if (pSysMenu != NULL)
	{
		BOOL bNameValid;
		CString strAboutMenu;
		bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
		ASSERT(bNameValid);
		if (!strAboutMenu.IsEmpty())
		{
			pSysMenu->AppendMenu(MF_SEPARATOR);
			pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
		}
	}

	// Set the icon for this dialog.  The framework does this automatically
	//  when the application's main window is not a dialog
	SetIcon(m_hIcon, TRUE);			// Set big icon
	SetIcon(m_hIcon, FALSE);		// Set small icon

	m_spinClusterCount.SetBuddy(&m_txtClusterCount);
	m_spinClusterCount.SetRange32(2, 20);
	m_spinClusterCount.SetPos32(3);
	m_spinIterationCount.SetBuddy(&m_txtIterationCount);
	m_spinIterationCount.SetRange32(5, 200);
	m_spinIterationCount.SetPos32(10);
	m_txtEpsilon.SetWindowText(L"1.0");
	m_spinPatchSize.SetBuddy(&m_txtPatchSize);
	m_spinPatchSize.SetRange(8, 32);
	m_spinPatchSize.SetPos(16);

	m_imlRepresentative.Create(16, 16, ILC_COLOR24, 0, 1);
	m_imlCodeBook.Create(16, 16, ILC_COLOR24, 0, 1);

	m_lstRepresentative.InsertColumn(0, L"Representative", LVCFMT_LEFT/* | LVCFMT_IMAGE*/, 125);
	m_lstRepresentative.InsertColumn(1, L"Cluster", LVCFMT_LEFT, 60);
	m_lstRepresentative.InsertColumn(2, L"Count", LVCFMT_LEFT, 60);
	m_lstRepresentative.InsertColumn(3, L"Feature", LVCFMT_LEFT, 300);

	m_cmbFeature.SetCurSel(0);

	ListView_SetExtendedListViewStyleEx(m_lstRepresentative.GetSafeHwnd(),
		0, LVS_EX_FULLROWSELECT);
	ListView_SetExtendedListViewStyleEx(m_lstCodeBook.GetSafeHwnd(),
		0, LVS_EX_FULLROWSELECT);
	return TRUE;  // return TRUE  unless you set the focus to a control
}

void CPatchClusterDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
	if ((nID & 0xFFF0) == IDM_ABOUTBOX)
	{
		CAboutDlg dlgAbout;
		dlgAbout.DoModal();
	}
	else
	{
		CDialog::OnSysCommand(nID, lParam);
	}
}

void CPatchClusterDlg::OnPaint()
{
	if (IsIconic())
	{
		CPaintDC dc(this); // device context for painting

		SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);

		// Center icon in client rectangle
		int cxIcon = GetSystemMetrics(SM_CXICON);
		int cyIcon = GetSystemMetrics(SM_CYICON);
		CRect rect;
		GetClientRect(&rect);
		int x = (rect.Width() - cxIcon + 1) / 2;
		int y = (rect.Height() - cyIcon + 1) / 2;

		// Draw the icon
		dc.DrawIcon(x, y, m_hIcon);
	}
	else
	{
		CDialog::OnPaint();
	}
}

HCURSOR CPatchClusterDlg::OnQueryDragIcon()
{
	return static_cast<HCURSOR>(m_hIcon);
}

void CPatchClusterDlg::OnBnClickedBtnbrowsefolder()
{
	CString sPath = BrowseFolder(L"Where is your referees?");
	m_txtImgFolder.SetWindowText(sPath);
}

void CPatchClusterDlg::OnBnClickedBtnbrowseoutputfolder()
{
	CString sFolder = BrowseFolder(L"Where to put the output data (codebook small images)?");
	m_txtCodeBookPath.SetWindowText(sFolder);
}

void CPatchClusterDlg::OnBnClickedOk()
{
	/*CString sPath;
	m_txtImgFolder.GetWindowText(sPath);

	CFeatureExtract* extractor = NULL;
	if(m_cmbFeature.GetCurSel() == 0)
	{
		extractor = new CHistFeature();
		((CHistFeature*)extractor)->SetHistSize(32);
	}
	else
		extractor = new CMomentFeature();

	CString sEpsilon;
	m_txtEpsilon.GetWindowText(sEpsilon);

	CKmeansCluster* cluster = new CKmeansCluster();
	cluster->ClusterCount = m_spinClusterCount.GetPos32();
	cluster->IterationCount = m_spinIterationCount.GetPos32();
	cluster->Epsilon = _wtof(sEpsilon);

	if(m_codeBook)
		delete m_codeBook;
	CCodeBookGenerator generator;
	m_codeBook = generator.CreateCodeBook(sPath, extractor, cluster);
	delete extractor;
	delete cluster;

	RenderCodeBook();

	CString sFile;
	m_txtXmlFile.GetWindowText(sFile);
	if(sFile.GetLength())
	{
		CMarkup xml;
		m_codeBook->Serialize(xml);

		CTextFileWrite f(sFile);
		if(f.IsOpen())
		{
			f.Write(xml.GetDoc());
			f.Close();
		}

		CTextFileRead fRead(sFile);
		if(fRead.IsOpen())
		{
			CString sXml;
			fRead.Read(sXml);
			CMarkup xmlRead(sXml);
			if(xmlRead.IsWellFormed())
			{
				CCodeBook* cb = CCodeBook::Deserialize(xmlRead);
				delete cb;
			}
		}
		
	}*/
	CDialog::OnOK();
}

void CPatchClusterDlg::OnLvnItemActivateLstrepresantative(NMHDR *pNMHDR, LRESULT *pResult)
{
	LPNMITEMACTIVATE pNMIA = reinterpret_cast<LPNMITEMACTIVATE>(pNMHDR);
	*pResult = 0;

	if(m_codeBook && 
		pNMIA->iItem >= 0 && pNMIA->iItem < m_lstRepresentative.GetItemCount())
	{
		CCodeBookEntry* entry = m_codeBook->Entries[pNMIA->iItem];

		m_lstCodeBook.DeleteAllItems();
		EmptyImgList(m_imlCodeBook);
		m_lstCodeBook.SetImageList(&m_imlCodeBook, LVSIL_SMALL);
		m_lstCodeBook.SetImageList(&m_imlCodeBook, LVSIL_NORMAL);

		for(int i = entry->Words.GetCount() - 1; i >= 0; --i)
		{
			CPatchSample* word = entry->Words[i];
			Gdiplus::Bitmap bmp(word->SamplePath);
			HBITMAP hBmp;
			bmp.GetHBITMAP(Gdiplus::Color(0, 0, 0), &hBmp);

			CString sFile;
			sFile = word->SamplePath;
			sFile = sFile.Mid(1 + sFile.ReverseFind(L'\\'));
			int iImgIdx = ImageList_Add(m_imlCodeBook, hBmp, NULL);
			m_lstCodeBook.InsertItem(0, sFile, iImgIdx);
		}
	}
}

void CPatchClusterDlg::OnBnClickedBtnbrowsexmlfile()
{
	CFileDialog dlg(FALSE, NULL, NULL, 
		OFN_PATHMUSTEXIST, 
		L"XML files (*.xml)|*.xml||", this);
	if(dlg.DoModal() == IDOK)
	{
		m_txtXmlFile.SetWindowText(dlg.GetPathName()/*m_ofn.lpstrFile*/);
	}
}
void CPatchClusterDlg::OnBnClickedBtnapprove()
{
	CString sCaption;
	m_btnStartFeature.GetWindowText(sCaption);
	if(sCaption.CompareNoCase(L"Start") == 0)
	{
		CString sFolder;
		m_txtImgFolder.GetWindowText(sFolder);

		if(sFolder.GetLength() <= 0)
			return;

		m_sFileFindPath = sFolder + 
			(sFolder[sFolder.GetLength() - 1] == L'\\' ? L".jpg" : L"\\*.jpg");
		
		if(!m_fileFinder.FindFile( m_sFileFindPath ))
			return;
	
		for(int i = m_arrFeatures.GetCount() - 1; i >= 0; --i)
		{
			CPatchSample* sample = m_arrFeatures[i];
			delete sample;
		}
		m_arrFeatures.RemoveAll();

		m_bFindingFile = m_fileFinder.FindNextFile();
		CString sFileName = m_fileFinder.GetFilePath();
		if(!this->LoadImageToEditor(sFileName))
			return;
		m_btnStartFeature.SetWindowText(L"Approve >>");
		m_btnClearHotRegion.EnableWindow(TRUE);
	}
	else
	{
		ProcessImageRegion();
		if(m_bFindingFile)
		{
			m_bFindingFile = m_fileFinder.FindNextFile();
			CString sFileName = m_fileFinder.GetFilePath();
			LoadImageToEditor(sFileName);
		}
		else
		{
			m_fileFinder.Close();
			m_btnStartFeature.SetWindowText(L"Start");
			m_btnClearHotRegion.EnableWindow(FALSE);
			MessageBox(L"Done, you can start clustering!");
		}

	}
}

void CPatchClusterDlg::OnBnClickedBtnclear()
{
	m_picFrame.ClearHotRegion();
}

void CPatchClusterDlg::OnBnClickedBtnstartcluster()
{
	if(m_codeBook)
		delete m_codeBook;

	CString sEpsilon;
	m_txtEpsilon.GetWindowText(sEpsilon);

	CKmeansCluster* cluster = new CKmeansCluster();
	cluster->ClusterCount = m_spinClusterCount.GetPos32();
	cluster->IterationCount = m_spinIterationCount.GetPos32();
	cluster->Epsilon = _wtof(sEpsilon);
	
	CCodeBookGenerator generator;
	m_codeBook = generator.CreateCodeBook(m_arrFeatures, cluster);

	delete cluster;

	RenderCodeBook();

	CString sFile;
	m_txtXmlFile.GetWindowText(sFile);
	if(sFile.GetLength())
	{
		CMarkup xml;
		m_codeBook->Serialize(xml);

		CTextFileWrite f(sFile);
		if(f.IsOpen())
		{
			f.Write(xml.GetDoc());
			f.Close();
		}

		CTextFileRead fRead(sFile);
		if(fRead.IsOpen())
		{
			CString sXml;
			fRead.Read(sXml);
			CMarkup xmlRead(sXml);
			if(xmlRead.IsWellFormed())
			{
				CCodeBook* cb = CCodeBook::Deserialize(xmlRead);
				delete cb;
			}
		}
	}
}

void CPatchClusterDlg::OnBnClickedBtnloadsamples()
{
	CFileDialog dlg(TRUE, NULL, NULL, 
		OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST, 
		L"XML files (*.xml)|*.xml||", this);
	if(dlg.DoModal() == IDOK)
	{
		CString sXmlFile = dlg.GetPathName();

		for(INT_PTR i = m_arrFeatures.GetCount() - 1; i >= 0; --i)
		{
			CPatchSample* sample = m_arrFeatures[i];
			delete sample;
		}
		m_arrFeatures.RemoveAll();

		int iCount = 0;
		CTextFileRead f(sXmlFile);
		if(f.IsOpen())
		{
			CString sXml;
			f.Read(sXml);
			CMarkup xmlRead(sXml);

			if(xmlRead.IsWellFormed())
			{
				if(xmlRead.FindElem(L"Samples"))
				{
					while(xmlRead.FindChildElem(L"PatchSample"))
					{
						CPatchSample* sample = CPatchSample::Deserialize(xmlRead);
						if(sample)
						{
							m_arrFeatures.Add(sample);
							++iCount;
						}
					}
				}
			}
			f.Close();
		}

		if(iCount > 0)
		{
			CString sMess;
			sMess.Format(L"Add %d samples successfully!", iCount);
			MessageBox(sMess, L"Congrats", MB_OK);
		}
		else
		{
			MessageBox(L"Loading was failed :(", L"Err...", MB_OK);
		}
	}
}

void CPatchClusterDlg::OnBnClickedBtnsavesample()
{
	CFileDialog dlg(FALSE, NULL, NULL, 
		OFN_PATHMUSTEXIST | OFN_OVERWRITEPROMPT, 
		L"XML files (*.xml)|*.xml||", this);
	if(dlg.DoModal() == IDOK)
	{
		CString sXmlFile = dlg.GetPathName();

		CMarkup xml;
		xml.AddElem(L"Samples");
		xml.IntoElem();
		for(INT_PTR i = m_arrFeatures.GetCount() - 1; i >= 0; --i)
		{
			m_arrFeatures[i]->Serialize(xml);
		}
		xml.OutOfElem();

		CTextFileWrite f(sXmlFile);
		if(f.IsOpen())
		{
			f.Write(xml.GetDoc());
			f.Close();
		}

		MessageBox(L"Done");
	}

}
